use crate::*;

/// Finalizer function for N-API objects implementing NapiDrop trait
pub unsafe extern "C" fn finalizer<T: NapiDrop + 'static>(
	env: sys::napi_env,
	data: *mut core::ffi::c_void,
	_: *mut ::std::os::raw::c_void,
) {
	let env = Env::from(env);
	let data = from_raw_data(data);
	let Ok(data) = data.downcast::<T>() else {
		log::error!("{}", Status::invalid_arg("finalizer downcast fail, drop_napi will not be called"));
		return;
	};
	data.drop_napi(env).throw(env);
}

/// Converts a value into raw void pointer for N-API storage
pub fn into_raw_data<T: 'static>(value: T) -> *mut core::ffi::c_void {
	let data: Box<dyn core::any::Any> = Box::new(value);
	let data = Box::new(data);
	let data = Box::into_raw(data);
	data as _
}

/// Recovers data from raw void pointer
pub unsafe fn from_raw_data(data: *mut core::ffi::c_void) -> Box<dyn core::any::Any> {
	let data = data as *mut Box<dyn core::any::Any>;
	Box::from_raw(data)
}

/// Gets an immutable reference to data from raw void pointer
pub unsafe fn ref_raw_data<T: 'static>(data: *mut core::ffi::c_void) -> Option<&'static T> {
	let data = data as *mut Box<dyn core::any::Any>;
	let data = &*data;
	data.downcast_ref()
}

/// Gets a mutable reference to data from raw void pointer
pub unsafe fn mut_raw_data<T: 'static>(data: *mut core::ffi::c_void) -> Option<&'static mut T> {
	let data = data as *mut Box<dyn core::any::Any>;
	let data = &mut *data;
	data.downcast_mut()
}

#[cfg(test)]
mod tests {
	use super::*;
	#[test]
	#[should_panic]
	fn test_raw_drop() {
		struct M;
		impl Drop for M {
			fn drop(&mut self) {
				panic!("")
			}
		}
		let raw = into_raw_data(M);
		let _ = unsafe { from_raw_data(raw) };
	}
	#[test]
	fn test_raw_ref() {
		struct M(u32);
		let raw = into_raw_data(M(100));
		assert!(unsafe { ref_raw_data::<M>(raw) }.unwrap().0 == 100);
		assert!(unsafe { mut_raw_data::<M>(raw) }.unwrap().0 == 100);
		let _ = unsafe { from_raw_data(raw) };
	}
}
